﻿/**
 *
 * resource.c
 *
 *  Created on: 2019-12-06
 *      Author: chenshisheng
 */

#include "resource.h"
#include "at_command.h"
#include "door.h"
#include "net.h"

static const struct _ResInfo
{
    int objId;
    int insCount;
    int resId;
    AtCommand_DataType_t type;
} _resInfos[] =
{
        {3322, 2, 5700, AtCommand_DataType_Float},
        {3342, 1, 5500, AtCommand_DataType_Bool},
        {3201, 1, 5550, AtCommand_DataType_Bool},
        {3200, 2, 5500, AtCommand_DataType_Bool},
        {3320, 1, 5700, AtCommand_DataType_Float},
        {3333, 1, 5506, AtCommand_DataType_Integer},
};

static struct
{
    bool objAdded;
    bool resDiscovered;
}_openInfos[ARRAY_SIZE(_resInfos)];

static struct _Profile
{
    Resource_Path_t path;
    AtCommand_DataType_t type;
}_profiles[Resource_Max];

static struct
{
    bool needNotify;
    AtCommand_Value_t value;
}_values[Resource_Max];

void Resource_Init(void)
{
    Resource_t res;
    unsigned int i;
    int c;

    res = 0;
    for(i = 0; i < ARRAY_SIZE(_resInfos); i++)
    {
        for(c = 0; (c < _resInfos[i].insCount) && (res < Resource_Max); c++)
        {
            _profiles[res].path.objId = _resInfos[i].objId;
            _profiles[res].path.insId = c;
            _profiles[res].path.resId = _resInfos[i].resId;
            _profiles[res].type = _resInfos[i].type;

            res ++;
        }
    }

    memset(_values, 0, sizeof(_values));
}

int Resource_MiplCreate(void)
{
    unsigned int i;

    for(i = 0; i < ARRAY_SIZE(_openInfos); i++)
    {
        _openInfos[i].objAdded = false;
        _openInfos[i].resDiscovered = false;
    }

    return AtCommand_MiplCreate();
}

int Resource_AddObj(void)
{
    int result, ret;
    unsigned int i;

    result = 0;
    for(i = 0; i < ARRAY_SIZE(_resInfos); i++)
    {
        if(_openInfos[i].objAdded)
        {
            continue;
        }

        ret = AtCommand_MiplAddObj(_resInfos[i].objId, _resInfos[i].insCount);
        if(ret == 0)
        {
            _openInfos[i].objAdded = true;
        }
        else if(result == 0)
        {
            result = ret;
        }

        HAL_Delay(20);
    }

    return result;
}

int Resource_DiscoverRsp(void)
{
    int result, ret;
    unsigned int i;

    result = 0;
    for(i = 0; i < ARRAY_SIZE(_resInfos); i++)
    {
        if(_openInfos[i].resDiscovered)
        {
            continue;
        }

        ret = AtCommand_MiplDiscoverRsp(_resInfos[i].objId, &_resInfos[i].resId, 1);
        if(ret == 0)
        {
            _openInfos[i].resDiscovered = true;
        }
        else if(result == 0)
        {
            result = ret;
        }

        HAL_Delay(20);
    }

    return result;
}

bool Resource_IsNeedNotify(void)
{
    Resource_t res;
    bool need = false;

    for(res = 0; res < Resource_Max; res++)
    {
        if(_values[res].needNotify)
        {
            need = true;
            break;
        }
    }

    return need;
}

int Resource_Update(Resource_t res, AtCommand_DataType_t type, AtCommand_Value_t value)
{
    if(res >= Resource_Max)
    {
        return -1;
    }

    if(type != _profiles[res].type)
    {
        return -2;
    }

    debug("Resource: update: %d\n", res);
    _values[res].needNotify = true;
    if((res == Resource_InputWeight) && _values[res].needNotify)
    {
        _values[res].value.fv += value.fv; // 叠加未上报的投递重量
    }
    else
    {
        _values[res].value = value;
    }

    Net_StartNotify();

    return 0;
}

static inline int _Notify(Resource_t res, AtCommand_Value_t value)
{
    return AtCommand_MiplNotify(
            _profiles[res].path.objId,
            _profiles[res].path.insId,
            _profiles[res].path.resId,
            _profiles[res].type,
            value);
}

void Resource_OnMiplDiscoverObj(const MsgQueue_Msg_t *msg)
{
    int objId;
    Resource_t res;
    unsigned int retry;
    int ret;

    objId = *((int *)msg->data);
    for(res = 0; res < Resource_Max; res++)
    {
        if(_profiles[res].path.objId != objId)
        {
            continue;
        }

        if(! _values[res].needNotify)
        {
            continue;
        }

        for(retry = 0; retry < 3; retry++)
        {
            ret = _Notify(res, _values[res].value);
            if(ret == 0)
            {
                _values[res].needNotify = false;
                ON_RELEASE(printf("Resource: %d notified.\n", (int) res);)
                break;
            }

            HAL_Delay(20);
        }
    }
}

int Resource_NotifyAll(void)
{
    Resource_t res;
    int result, ret;

    result = 0;
    for(res = 0; res < Resource_Max; res++)
    {
        if(! _values[res].needNotify)
        {
            continue;
        }

        ret = _Notify(res, _values[res].value);
        if(ret == 0)
        {
            _values[res].needNotify = false;
            ON_RELEASE(printf("Resource: %d notified.\n", (int) res);)
        }
        else if(result == 0)
        {
            result = ret;
        }
    }

    return result;
}

AtCommand_WriteResult_t Resource_OnWrite(const AtCommand_WriteInfo_t *info)
{
    const Resource_Path_t *p;
    int success;

    p = & _profiles[Resource_OpenDoor].path;
    if((info->objId == p->objId) && (info->insId == p->insId) && (info->resId == p->resId))
    {
        success = Door_Open();
    }
    else
    {
        return AtCommand_WriteResult_BadRequest;
    }

    return success ? AtCommand_WriteResult_Success : AtCommand_WriteResult_Unauthorized;
}

int Resource_NotifyCmd(Stream_t *stream, int argc, char *argv[])
{
    Resource_t res;
    AtCommand_DataType_t type;
    AtCommand_Value_t value;

    if(argc < 4)
    {
        Shell_PrintHelp(stream, argv[0]);
        for(type = AtCommand_DateType_Min; type < AtCommand_DateType_Max; type++)
        {
            chprintf(stream, "data type: %d -> %s\n", type, AtCommand_DateTypeString(type));
        }
    }
    else
    {
        res = (Resource_t)atoi(argv[1]);
        type = (AtCommand_DataType_t)atoi(argv[2]);

        switch(type)
        {
        case AtCommand_DataType_String:
        case AtCommand_DataType_Opaque:
        case AtCommand_DataType_HexString:
            value.sv = argv[3];
            break;

        case AtCommand_DataType_Integer:
            value.iv = atoi(argv[3]);
            break;

        case AtCommand_DataType_Float:
            value.fv = atoff(argv[3]);
            break;

        case AtCommand_DataType_Bool:
            value.bv = atoi(argv[3]) ? true : false;
            break;

        default:
            type = AtCommand_DataType_String;
            value.sv = argv[3];
            break;
        }

        chprintf(stream, "Start notification ...\n");
        Resource_Update(res, type, value);
    }

    return 0;
}

void Resource_OnHeartbeat(const MsgQueue_Msg_t *msg)
{
	unsigned int beat;
	AtCommand_Value_t value;

	beat = *((unsigned int *)msg->data);
	value.iv = (int)beat;
	Resource_Update(Resource_Time, AtCommand_DataType_Integer, value);

}
